
#include "BinomialTree.h"
#include <cmath>

//time should match the expiry of product, how to resolve this?
BinomialTree::BinomialTree(double spot, const Parameter& r, const Parameter& vol,
	double time, long steps)
	: m_spot(spot),m_r(r),m_vol(vol),m_time(time),m_steps(steps),m_treebuilt(false)
{

}
//consider Boost observer and notifier pattern
//If the parameters changed, the tree needs to be rebuilt
void BinomialTree::buildTree()
{
	//build the price process of the underlying asset
	m_tree.resize(m_steps+1);
	double init_log_spot = log(m_spot);
	double time_size = m_time/m_steps;
	for(long i=0; i<=m_steps; i++){
		m_tree[i].resize(i+1); //step i has i+1 nodes
		double current_time = i*time_size;
		double drift_log_spot = init_log_spot + m_r.integral(0.0,current_time);
		drift_log_spot -= 0.5*m_vol.integralsqaure(0.0,current_time);
		
		double sd = sqrt(m_vol.integralsqaure(0.0,time_size));
		for (long k=0;k <= i;k++) {
			long j = -i+2*k;
			m_tree[i][k].first = exp(drift_log_spot+j*sd);
		}
	}

	//build discount factors
	m_discounts.resize(m_steps);
	for (long i=0; i<m_steps; i++){
		m_discounts[i]=exp(-m_r.integral(i*time_size,(i+1)*time_size));
	}
	//tree has been bulit
	m_treebuilt = true;
}

double BinomialTree::calcPrice(const SolvableTree& product)
{
	if(!m_treebuilt)
		buildTree();
	if(product.getExpiry()!=m_time)
		throw "the expiry is mismatched between tree and product";
	//calculate final payoff
	double time_size = m_time/m_steps;
	for (long j = -m_steps,k=0;
		j <= m_steps;j+=2,k++) {
			m_tree[m_steps][k].second = product.calcFinalPayOff(m_tree[m_steps][k].first);
	}
	//calculate medium payoff
	for (long i = m_steps-1; i>=0; i--){
		double current_time = i*time_size;
		for (long k=0;k <= i;k++) {
			double spot = m_tree[i][k].first;
			double payoff = 0.5*m_tree[i+1][k].second+0.5*m_tree[i+1][k+1].second;
			double discounted_payoff = payoff*m_discounts[i];
			m_tree[i][k].second = product.calcMidPayOff(spot,current_time,discounted_payoff);
		}
	}
	return m_tree[0][0].second;
}
//need to indicate which greek
double BinomialTree::calcGreek(const SolvableTree& product)
{
	//hasn't been implemented yet
	return 0.0;
}

